using System.Collections.Generic;
using NaughtyAttributes;
using UnityEngine;
using UnityEngine.UI;

namespace Gj
{
    public class TextExtend : BaseMeshEffect
    {
        public GameObject prefix;
        public Vector3 prefixOffset;
        public GameObject badge;
        public Vector3 badgeOffset;
        public RectTransform rect;
        public Text text;

        [Header("是否开启对齐")] public bool useAlign;

        [Header("字间距")] public float wordSpace;

        [Header("行间距")] public float lineSpace;

        [Header("是否显示下划线")] public bool useUnderline = true;

        [Header("下划线是否忽略换行符")] public bool ignoreBreakSign = true;

        [Header("行高是否两端扩展,否则向下扩展")] public bool lineHeightJustify = true;

        public float lineOffset;

        [Header("横线对齐到行的上下中间")] public bool lineAlignToMiddle;

        // [Header("下划线宽度"), Range(0f, 100f), SerializeField]
        private float _lineHeight = 3f;

        private float _lineHeightHalf = 1.5f;

        // 可视的字符个数
        private int characterCountVisible;
        private UICharInfo[] characters;
        private readonly Color32 lineColor = Color.white;
        private UILineInfo[] lines;

        private List<UIVertex> stream;
        private char[] textChars;
        private readonly UIVertex[] underlineUIVertexs = new UIVertex[6];

        public float lineHeight
        {
            get => _lineHeight;
            set
            {
                _lineHeight = value;
                _lineHeightHalf = value * .5f;
            }
        }

        [Button]
        public void Refresh()
        {
            if (badge != null || prefix != null)
            {
                var pivot = rect.pivot;
                if (badge != null)
                    badge.transform.localPosition =
                        new Vector3(rect.rect.width * (1 - pivot.x), rect.rect.height * (1 - pivot.y), 0) + badgeOffset;
                if (prefix != null)
                    prefix.transform.localPosition = new Vector3(rect.rect.width * pivot.x * -1, 0, 0) + prefixOffset;
            }
        }

        /// 从font纹理中获取指定字符的uv
        private Vector2 GetUnderlineCharUV()
        {
            CharacterInfo info;
            if (text.font.GetCharacterInfo('*', out info, text.fontSize, text.fontStyle))
                return (info.uvBottomLeft + info.uvBottomRight + info.uvTopLeft + info.uvTopRight) * 0.25f;
            Debug.LogWarning("GetCharacterInfo failed");
            return Vector2.zero;
        }

        public override void ModifyMesh(VertexHelper vh)
        {
            if (!useUnderline && !useAlign) return;

            // 当宽或高足够小则不处理
            if (text.rectTransform.rect.size.x <= 0 || text.rectTransform.rect.size.y <= 0) return;

#if UNITY_EDITOR
            _lineHeightHalf = lineHeight * .5f;
#endif

            // cachedTextGenerator是当前实际显示出来的相关信息,cachedTextGeneratorForLayout是所有布局信息(包括看不到的)
            characters = text.cachedTextGenerator.GetCharactersArray();
            lines = text.cachedTextGenerator.GetLinesArray();
            if (useUnderline && ignoreBreakSign) textChars = text.text.ToCharArray();

            // 使用characterCountVisible来得到真正显示的字符数量.characterCount会额外包含(在宽度不足)时候裁剪掉的(边缘)字符,会导致显示的下划线多一个空白的宽度
            characterCountVisible = text.cachedTextGenerator.characterCountVisible;

            /* stream是6个一组(对应三角形索引信息,左上角开始,顺时针)对应1个字(含空白)
                0-1
                 \|
                  2
                0
                |\
                3-2
                数组索引:
                [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...]
                对应三角形索引:
                [0, 1, 2, 2, 3, 0, 4, 5, 6, 6,  7,  4,...]
            */
            stream = new List<UIVertex>();

            vh.GetUIVertexStream(stream);
            vh.Clear();

            if (useAlign)
            {
                var _offi = 0;
                for (var i = 0; i < stream.Count; i += 6) // 单个字符
                {
                    var charIdx = i / 6;

                    var offx = 0f;
                    var offy = 0f;
                    if (useAlign)
                    {
                        // 不能直接使用(wordSpace * charIdx)来计算每个字符在间距下的x偏移,
                        // 要判断该字符是否处于换行的第1个字符序列,如果是第1个应该重置为0,
                        // GetCharCursorPos()中在获取每个字符的起始位置时,也会将该偏移减掉.
                        var lineIndex = GetCharInLineIndex(charIdx);
                        if (lines[lineIndex].startCharIdx == charIdx) _offi = 0;

                        offx = wordSpace * _offi;
                        offy = lineSpace * lineIndex;

                        _offi++;
                    }

                    for (var j = 0; j < 6; j++)
                        // 间距
                        if (useAlign)
                            DoAlign(i, j, offx, offy);
                }
            }

            DrawAllLinesLine();

            vh.AddUIVertexTriangleStream(stream);
        }

        private void DoAlign(int i, int j, float offx, float offy)
        {
            var uiv = stream[i + j];
            var pos = uiv.position;
            // pos.x += this.wordSpace * _offi;
            // pos.y += this.lineSpace * lineIndex;
            pos.x += offx;
            pos.y += offy;

            uiv.position = pos;
            stream[i + j] = uiv;
        }

        /// 获取一个字符索引所在的行
        private int GetCharInLineIndex(int charIndex)
        {
            var ei = lines.Length - 1;
            for (var i = 0; i < ei; i++)
            {
                var line = lines[i];
                if (charIndex >= line.startCharIdx && charIndex < lines[i + 1].startCharIdx) return i;
            }

            // 是否在最后一行
            if (charIndex >= lines[ei].startCharIdx && charIndex < characters.Length) return ei;

            return -1;
        }

        // 显示所有下划线
        private void DrawAllLinesLine()
        {
            var uv0 = GetUnderlineCharUV();
            for (var i = 0; i < lines.Length; i++)
            {
                var line = lines[i];
                var endIndex = 0;
                if (i + 1 < lines.Length)
                {
                    // 通过下一行的起始索引减1得到这一行最后一个字符索引位置
                    var nextLineStartCharIdx = lines[i + 1].startCharIdx;
                    // 与本行的相同,当文本宽度只够容纳一个字的时候,unity会产生一个空行,要排除改行
                    if (nextLineStartCharIdx == lines[i].startCharIdx) continue;

                    endIndex = nextLineStartCharIdx - 1;
                }
                else
                {
                    // 最后一行的最后字符索引位置
                    if (characterCountVisible == 0) continue;
                    endIndex = characterCountVisible - 1;
                }

                var bottomY = GetLineBottomY(i);

                var firstCharOff = line.startCharIdx * wordSpace;
                AddUnderlineVertTriangle(line.startCharIdx, endIndex, firstCharOff, bottomY, uv0);
            }
        }

        private float GetLineBottomY(int lineIndex)
        {
            var line = lines[lineIndex];
            var bottomY = line.topY - (lineAlignToMiddle ? line.height * .5f : line.height) - lineOffset;

            // bottomY是原始大小下的信息,但文本在不同分辨率下会被进一步缩放处理,所以要将比例带入计算
            bottomY /= text.pixelsPerUnit;

            if (useAlign) bottomY += lineIndex * lineSpace;
            return bottomY;
        }

        private Vector2 GetCharCursorPos(int charIdx, float firstCharOff)
        {
            var charInfo = characters[charIdx];
            var cursorPos = charInfo.cursorPos;
            // cursorPos是原始大小下的信息,但文本在不同分辨率下会被进一步缩放处理,所以要将比例带入计算
            cursorPos /= text.pixelsPerUnit;

            var rtf = transform as RectTransform;
            if (useAlign)
                // 行的第1个字符在DoAlign()中是修正过偏移的,所有后面的所有字符都要相应使用此偏移
                cursorPos.x += wordSpace * charIdx - firstCharOff;
            return cursorPos;
        }

        private void AddUnderlineVertTriangle(int startIndex, int endIndex, float firstCharOff, float bottomY,
            Vector2 uv0)
        {
            if (ignoreBreakSign && textChars[endIndex] == '\n')
                // 跳过换行符
                endIndex--;
            if (endIndex < startIndex) return;

            // 左上
            var pos0 = new Vector3(GetCharCursorPos(startIndex, firstCharOff).x,
                bottomY + (lineHeightJustify ? _lineHeightHalf : 0), 0);

            // 左下, 向下扩展
            var pos1 = new Vector3(pos0.x, pos0.y - lineHeight, 0);

            // 右下. charWidth是原始大小下的信息,但文本在不同分辨率下会被进一步缩放处理,所以要将比例带入计算
            var pos2 = new Vector3(
                GetCharCursorPos(endIndex, firstCharOff).x + characters[endIndex].charWidth / text.pixelsPerUnit,
                pos1.y, 0);

            // 右上
            var pos3 = new Vector3(pos2.x, pos0.y, 0);

            // 按照stream存储的规范,构建6个顶点: 左上和右下是2个三角形的重叠, 
            var vert = UIVertex.simpleVert;
            vert.color = lineColor;
            vert.uv0 = uv0;

            vert.position = pos0;
            underlineUIVertexs[0] = vert;
            underlineUIVertexs[3] = vert;

            vert.position = pos1;
            underlineUIVertexs[5] = vert;

            vert.position = pos2;
            underlineUIVertexs[2] = vert;
            underlineUIVertexs[4] = vert;

            vert.position = pos3;
            underlineUIVertexs[1] = vert;

            stream.AddRange(underlineUIVertexs);
        }
    }
}